package com.Unidbg_hengdin.ndk;

import com.alibaba.fastjson.util.IOUtils;
//import com.bytedance.frameworks.core.encrypt.TTEncrypt;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.Symbol;
import com.github.unidbg.arm.HookStatus;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.backend.CodeHook;
import com.github.unidbg.arm.backend.UnHook;
import com.github.unidbg.arm.backend.Unicorn2Factory;
import com.github.unidbg.arm.context.Arm32RegisterContext;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.hook.HookContext;
import com.github.unidbg.hook.ReplaceCallback;

import com.github.unidbg.hook.hookzz.Dobby;
import com.github.unidbg.hook.hookzz.HookEntryInfo;
import com.github.unidbg.hook.hookzz.HookZz;
import com.github.unidbg.hook.hookzz.IHookZz;
import com.github.unidbg.hook.hookzz.InstrumentCallback;
import com.github.unidbg.hook.hookzz.WrapCallback;
import com.github.unidbg.hook.xhook.IxHook;
import com.github.unidbg.hook.hookzz.*;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.XHookImpl;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.utils.Inspector;
import com.sun.jna.Pointer;

import java.io.File;




public class NativeHelper {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    private final DvmClass NativeHelperUtils;

    private final boolean logging;

    NativeHelper(boolean logging) {
        this.logging = logging;

        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xiaojianbang.app")
                .addBackendFactory(new Unicorn2Factory(true))
                .build(); // 创建模拟器实例，要模拟32位或者64位，在这里区分
        final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析


        vm = emulator.createDalvikVM(); // 创建Android虚拟机
        vm.setVerbose(logging); // 设置是否打印Jni调用细节      unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbang.so
        vm.setJni(new AbstractJni() {
            @Override
            public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
                System.out.println("signature : " + signature);
                if(signature.equals("java/lang/String->getBytes(Ljava/lang/String;)[B")){
                    String args = (String)dvmObject.getValue();

                    System.out.println("args :" + args);
                    byte[] byteArray = args.getBytes();
                    return new ByteArray(vm,byteArray);
                }
                return super.callObjectMethodV(vm, dvmObject, signature, vaList);
            }
        });

//        DalvikModule dmA = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbangA.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndk/libxiaojianbang.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
//        dm.callJNI_OnLoad(emulator); // 手动执行JNI_OnLoad函数
        module = dm.getModule(); // 加载好的libttEncrypt.so对应为一个模块

        NativeHelperUtils = vm.resolveClass("com/xiaojianbang/ndk/NativeHelper");
    }

    void destroy() {
        IOUtils.close(emulator);
        if (logging) {
            System.out.println("destroy");
        }
    }

    public static void main(String[] args) throws Exception {
        NativeHelper test = new NativeHelper(true);

//        int data = test.callFun();
        test.callFun();

//        Inspector.inspect(data, "callFun");
//
        test.destroy();
    }

    void callFun() {
//        if (logging) {
//            Symbol sbox0 = module.findSymbolByName("sbox0"); // 在libttEncrypt.so模块中查找sbox0导出符号
//            Symbol sbox1 = module.findSymbolByName("sbox1");
//            Inspector.inspect(sbox0.createPointer(emulator).getByteArray(0, 256), "sbox0"); // 打印sbox0导出符号在unicorn中的内存数据
//            Inspector.inspect(sbox1.createPointer(emulator).getByteArray(0, 256), "sbox1");
//
//            IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz，支持inline hook，文档看https://github.com/jmpews/HookZz
//            hookZz.enable_arm_arm64_b_branch(); // 测试enable_arm_arm64_b_branch，可有可无
//            hookZz.wrap(module.findSymbolByName("ss_encrypt"), new WrapCallback<RegisterContext>() { // inline wrap导出函数
//                @Override
//                public void preCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
//                    Pointer pointer = ctx.getPointerArg(2);
//                    int length = ctx.getIntArg(3);
//                    byte[] key = pointer.getByteArray(0, length);
//                    Inspector.inspect(key, "ss_encrypt key");
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, RegisterContext ctx, HookEntryInfo info) {
//                    System.out.println("ss_encrypt.postCall R0=" + ctx.getLongArg(0));
//                }
//            });
//            hookZz.disable_arm_arm64_b_branch();
//            hookZz.instrument(module.base + 0x00000F5C + 1, new InstrumentCallback<Arm32RegisterContext>() {
//                @Override
//                public void dbiCall(Emulator<?> emulator, Arm32RegisterContext ctx, HookEntryInfo info) { // 通过base+offset inline wrap内部函数，在IDA看到为sub_xxx那些
//                    System.out.println("R3=" + ctx.getLongArg(3) + ", R10=0x" + Long.toHexString(ctx.getR10Long()));
//                }
//            });
//
//            Dobby dobby = Dobby.getInstance(emulator);
//            dobby.replace(module.findSymbolByName("ss_encrypted_size"), new ReplaceCallback() { // 使用Dobby inline hook导出函数
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
//                    System.out.println("ss_encrypted_size.onCall arg0=" + context.getIntArg(0) + ", originFunction=0x" + Long.toHexString(originFunction));
//                    return HookStatus.RET(emulator, originFunction);
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, HookContext context) {
//                    System.out.println("ss_encrypted_size.postCall ret=" + context.getIntArg(0));
//                }
//            }, true);
//
//            IxHook xHook = XHookImpl.getInstance(emulator); // 加载xHook，支持Import hook，文档看https://github.com/iqiyi/xHook
//            xHook.register("libttEncrypt.so", "strlen", new ReplaceCallback() { // hook libttEncrypt.so的导入函数strlen
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, HookContext context, long originFunction) {
//                    Pointer pointer = context.getPointerArg(0);
//                    String str = pointer.getString(0);
//                    System.out.println("strlen=" + str);
//                    context.push(str);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//                @Override
//                public void postCall(Emulator<?> emulator, HookContext context) {
//                    System.out.println("strlen=" + context.pop() + ", ret=" + context.getIntArg(0));
//                }
//            }, true);
//            xHook.register("libttEncrypt.so", "memmove", new ReplaceCallback() {
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, long originFunction) {
//                    RegisterContext context = emulator.getContext();
//                    Pointer dest = context.getPointerArg(0);
//                    Pointer src = context.getPointerArg(1);
//                    int length = context.getIntArg(2);
//                    Inspector.inspect(src.getByteArray(0, length), "memmove dest=" + dest);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//            });
//            xHook.register("libttEncrypt.so", "memcpy", new ReplaceCallback() {
//                @Override
//                public HookStatus onCall(Emulator<?> emulator, long originFunction) {
//                    RegisterContext context = emulator.getContext();
//                    Pointer dest = context.getPointerArg(0);
//                    Pointer src = context.getPointerArg(1);
//                    int length = context.getIntArg(2);
//                    Inspector.inspect(src.getByteArray(0, length), "memcpy dest=" + dest);
//                    return HookStatus.RET(emulator, originFunction);
//                }
//            });
//            xHook.refresh(); // 使Import hook生效
//        }
//
//        if (logging) {
//            emulator.attach(DebuggerType.ANDROID_SERVER_V7); // 附加IDA android_server，可输入c命令取消附加继续运行
//        }
//        byte[] data = new byte[16];
//        --------------------------------------调用函数方式一 calllJniMethod  ---------------------------------------------
//        调用 add 方法
        int retval = NativeHelperUtils.callStaticJniMethodInt(emulator, "add(III)I", 0x100, 0x200, 0x300); // 执行Jni方法
        System.out.println("res : 0x" + Integer.toHexString(retval));

//        调用MD5方法
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());

//        调用encode 方法
//        StringObject encodeRetval = NativeHelperUtils.callStaticJniMethodObject(emulator, "encode()Ljava/lang/String;"); // 执行Jni方法
//        System.out.println("encodeRetval res : " + encodeRetval.getValue());


//        return md5_retval;
        // ---------------------------------- 调用函数方式二 symbol.call 方式 ----------------------------------------
//        通过符号调用函数
        // 符号名称需要是ida中拼接后的名称，因为此时，符号名称的拼接已经完成。
//        Symbol symbol = module.findSymbolByName("Java_com_xiaojianbang_ndk_NativeHelper_add", false);
//        // call方法 参数如下 (Emulator<?> emulator,Object... arg),第一个是 emulator，后续为函数参数，函数参数中，第一个env，第二个是jclass或者jobject,后续才是实际参数
//        // 返回值为 number 数组，第一个值为结果。 1. 如果是基本数据类型，该结果就是数据结果，2. 就是结果对应的hashcode，通过这个hashcode，再从虚拟机内存中获取到实际值。
//        Number[] numbers = new Number[]{symbol.call(emulator, vm.getJNIEnv(), vm.addLocalObject(NativeHelperUtils), 100, 200, 300)};
//        //
//        int res = numbers[0].intValue();
//        System.out.println("add_res : "+ res);
//
//        Symbol symbol_strcat = module.findSymbolByName("_Z7_strcatP7_JNIEnvP7_jclass", false);
//        // 返回值为 number 数组，第一个值为结果。 1. 如果是基本数据类型，该结果就是数据结果，2. 就是结果对应的hashcode，通过这个hashcode，再从虚拟机内存中获取到实际值。
//        Number[] numbers1 = new Number[]{symbol_strcat.call(emulator, vm.getJNIEnv(), vm.addLocalObject(NativeHelperUtils))};
//        int hashcode = (int) numbers1[0].intValue();
//        // 由于这个返回值的结果不是基本数据类型，因此需要从虚拟机内存中，通过getObject 获取，并通过getValue 获取到实际值。
//        String res_strcat = (String) vm.getObject(hashcode).getValue();
//        System.out.println(res_strcat);
//        ---------------------------------------- 调用函数方式三  module.callFunction -----------------------------------
        Number[] numbers1  = new Number[]{module.callFunction(emulator, "_Z7_strcatP7_JNIEnvP7_jclass", vm.getJNIEnv(), vm.addLocalObject(NativeHelperUtils))};
//        int hashcode = (int) numbers1[0].intValue();
////        // 由于这个返回值的结果不是基本数据类型，因此需要从虚拟机内存中，通过getObject 获取，并通过getValue 获取到实际值。
//        String res_strcat = (String) vm.getObject(hashcode).getValue();
//        System.out.println(res_strcat);

        // ---------------------- 确认 vm.addGlobalObject(obj) 的返回值 为 obj 的hashcode ---------------------------------
//        DvmObject<?> obj =  NativeHelperUtils.newObject(null);
//        System.out.println(vm.addGlobalObject(obj));
//        System.out.println(obj.hashCode());
        // 通过函数地址调用函数 & 特殊参数的构建 -----------------------------------------------------------------------------
//        Number[] numbers1  = new Number[]{module.callFunction(emulator, 0x1B4C, vm.getJNIEnv(), vm.addLocalObject(NativeHelperUtils))};
//        int hashcode = (int) numbers1[0].intValue();
////        // 由于这个返回值的结果不是基本数据类型，因此需要从虚拟机内存中，通过getObject 获取，并通过getValue 获取到实际值。
//        String res_strcat = (String) vm.getObject(hashcode).getValue();
//        System.out.println(res_strcat);
//
//        // init 2230
//        UnidbgPointer Md5Ctx = emulator.getMemory().malloc(200,false).getPointer();
//        module.callFunction(emulator,0x2230,Md5Ctx);
//        // update 22A0
//        byte[] buffer = "unidbg_study".getBytes();
//        UnidbgPointer plainText = emulator.getMemory().malloc(200,false).getPointer();
//        plainText.write(buffer);
//        module.callFunction(emulator,0x22A0,Md5Ctx,plainText, buffer.length);
//
//        // final 3A78
//        UnidbgPointer ciperText = emulator.getMemory().malloc(200,false).getPointer();
//        module.callFunction(emulator,0x3A78,Md5Ctx,ciperText);
//        byte[] res = ciperText.getByteArray(0,16);
//        Inspector.inspect(res,"MD5 res");

//        ---------------------------------------------- HOOK -------------------------------------------------------
//        ---------------------------------------------- HOOK -------------------------------------------------------
//        ---------------------------------------------- HOOK -------------------------------------------------------

//        ---------------------------------------------- wrap hook ----------------------------------------------
        // hozz   MD5------------------------------------------------------------------------------------------
        IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz，支持inline hook，文档看https://github.com/jmpews/HookZz
        hookZz.enable_arm_arm64_b_branch(); // 测试enable_arm_arm64_b_branch，可有可无

        // wrap 是重载函数，第一个参数为 symbol 或者 函数地址，两者的本质是一样的，在symbol的重载中，也是通过symbol 获取到函数地址
        // 第二个参数为一个 WrapCallback 实例，需要实现 preCall ，postCall 这两个方法，类似于 frida 中的 onEnter, onLeave
        // RegisterContext 是 Arm32RegisterContext ,Arm64RegisterContext 的父类，在 Arm32RegisterContext ,Arm64RegisterContext  中有实现一部分 RegisterContext 所没有的方法
        // 在 RegisterContext 中用于获取 函数实参。1. getPointerArg 获取指针参数，2. getIntArg 获取int 参数。 这两个方法的参数为 实参在参数中的索引值。
        hookZz.wrap(module.findSymbolByName("_Z9MD5UpdateP7MD5_CTXPhj"), new WrapCallback<Arm64RegisterContext>() { // inline wrap导出函数
            Pointer md5_ctx_pointer;
            @Override
            public void preCall(Emulator<?> emulator, Arm64RegisterContext ctx, HookEntryInfo info) {
                md5_ctx_pointer = ctx.getPointerArg(0);
                Pointer plainText_pointer = ctx.getPointerArg(1);
                int length = ctx.getIntArg(2);
                Inspector.inspect(md5_ctx_pointer.getByteArray(0,64), "preCall md5_ctx_pointer ");
                Inspector.inspect(plainText_pointer.getByteArray(0,length), "preCall plainText_pointer ");
            }
            @Override
            public void postCall(Emulator<?> emulator, Arm64RegisterContext ctx, HookEntryInfo info) {
                Inspector.inspect(md5_ctx_pointer.getByteArray(0,64), "preCall md5_ctx_pointer ");
            }
        });
        //        主动调用MD5方法,触发hook
        StringObject md5_retval_hook = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
        System.out.println("md5_retval_hook res : " + md5_retval_hook.getValue());
//      -------------------------------------------- inline hook ----------------------------------------------
////        hookZz.disable_arm_arm64_b_branch();
        hookZz.instrument(module.base + 0x1AEC , new InstrumentCallback<Arm64RegisterContext>() {
            @Override
            public void dbiCall(Emulator<?> emulator, Arm64RegisterContext ctx, HookEntryInfo info) { // 通过base+offset inline wrap内部函数，在IDA看到为sub_xxx那些
                System.out.println("w8=0x" + Integer.toHexString(ctx.getXInt(8)) + ", w9=0x" + Integer.toHexString(ctx.getXInt(9)));
            }
        });
        int retval_add = NativeHelperUtils.callStaticJniMethodInt(emulator, "add(III)I", 0x100, 0x200, 0x300); // 执行Jni方法
        System.out.println("res : 0x" + Integer.toHexString(retval_add));

        // hooz hook  jstring2cstr------------------------------------------------------------------------------------------
        IHookZz hookZzz = HookZz.getInstance(emulator); // 加载HookZz，支持inline hook，文档看https://github.com/jmpews/HookZz
//        hookZzz.wrap(module.findSymbolByName("_Z12jstring2cstrP7_JNIEnvP8_jstring"), new WrapCallback<Arm64RegisterContext>() { // inline wrap导出函数
//            Pointer md5_ctx_pointer;
//
//            @Override
//            public void preCall(Emulator<?> emulator, Arm64RegisterContext ctx, HookEntryInfo info) {
//                // 获取目标参数的 hashcode，从而通过 vm.getObject 获取该数据
//                int hashcode = ctx.getIntArg(1);
//                String res = (String) vm.getObject(hashcode).getValue();
//                System.out.println("preCall jstring :"+ res);
//            }
//            @Override
//            public void postCall(Emulator<?> emulator, Arm64RegisterContext ctx, HookEntryInfo info) {
//                Pointer ptr_res = ctx.getPointerArg(0);
//                // 访问寄存器，可以这样读取指针
////                Pointer ptr_res = ctx.getXPointer(0);
//
//                // c类型数据，需要通过 getByteArray 来读取数据
//                byte[] res_byte= ptr_res.getByteArray(0,16);
//                Inspector.inspect(res_byte,"postCall" );
//
//                // 如果结果是String 也可以使用 getString 方法来直接读取
//                String res =ptr_res.getString(0);
//                System.out.println("postCall jstring : " + res);
//
//            }
//        });
        Number[] numbers = new Number[]{module.callFunction(emulator, "_Z12jstring2cstrP7_JNIEnvP8_jstring", vm.getJNIEnv(), vm.addLocalObject(new StringObject(vm, "hengdihengdi")))};
        int c_addr = numbers[0].intValue();

        // 返回的是 c 类型的数据，通过指针 getByteArray 方法读取数据
        byte[] res = emulator.getMemory().pointer(c_addr).getByteArray(0,16);

        Inspector.inspect(res,"call function ");
        System.out.println("666666666");

        // 试图通过 vm.getObject(c_addr) 获取对象，本质上等同于尝试将 C 指针强行解释为 Java 对象引用‌，此时 Unidbg 无法识别该指针对应的对象，因此返回 null。
//        StringObject j_res = vm.getObject(c_addr);
//        System.out.println("new j_res" + j_res.getValue());
        // 使用 hookzz 寄存器
        hookZz.wrap(module.findSymbolByName("_Z12jstring2cstrP7_JNIEnvP8_jstring"), new WrapCallback<HookZzArm64RegisterContext>() { // inline wrap导出函数
            Pointer md5_ctx_pointer;
            @Override
            public void preCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
                int hashcode = ctx.getIntArg(1);
                String res = (String) vm.getObject(hashcode).getValue();
                System.out.println("preCall :"+ res);
            }
            @Override
            public void postCall(Emulator<?> emulator, HookZzArm64RegisterContext ctx, HookEntryInfo info) {
                int hashcode = vm.addLocalObject(new StringObject(vm,"dongfanghong"));
                // 修改 返回值
                ctx.setXLong(0,hashcode);

            }
        });
        Number[] numbers2 = new Number[]{module.callFunction(emulator, "_Z12jstring2cstrP7_JNIEnvP8_jstring", vm.getJNIEnv(), vm.addLocalObject(new StringObject(vm, "hengdihengdi")))};
        int c_addr2 = numbers2[0].intValue();
        // 修改返回值后，返回的数据是 unidbg 封装的object 类型，因此需要通过 vm.getObject 来获取
        StringObject res2 = vm.getObject(c_addr2);
        System.out.println("call function2 "+ res2.getValue());

        // --------------------------------------------- inline hook repalce -------------------------------------------
//        IHookZz hookZz = HookZz.getInstance(emulator); // 加载HookZz，支持inline hook，文档看https://github.com/jmpews/HookZz
//        hookZz.replace(module.findSymbolByName("Java_com_xiaojianbang_ndk_NativeHelper_md5"), new ReplaceCallback() {
//            @Override
//            public HookStatus onCall(Emulator<?> emulator, long originFunction) {
//                // 调用原函数的两种方式
////                return super.onCall(emulator, originFunction);
////                return HookStatus.RET(emulator,originFunction);
//
//
//                // 返回object,需要从 vm的内存中去读取。因此需要将其加入到内存中
////                int hashcode = vm.addLocalObject(new StringObject(vm,"python java unidbg"));
////                return HookStatus.LR(emulator, hashcode);
//
//                // 直接返回数值
//                return HookStatus.LR(emulator, 1000);
//
//            }
//        });
//        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval.getValue());

//        int md5_retval = NativeHelperUtils.callStaticJniMethodInt(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
//        System.out.println("md5_retval res : " + md5_retval);

        // unicorn 原生hook ---------------------------------------------------------------------------------------------
        // hook_add_new 支持多种hook， new CodeHook 是指令级别的hook
//        emulator.getBackend().hook_add_new(new CodeHook() {
//            // hook 方法中的参数
//                // address 表示当前指令的内存地址
//                // size 表示当前的指令长度，基本不会用到
//                // user 从 hook_add_new 中传进来的对象，可以作为标识。
//            @Override
//            public void hook(Backend backend, long address, int size, Object user) {
//                System.out.println("unicorn --------------------------");
//                System.out.println(size);
//                System.out.println(user);
//                // 获取到寄存器
//                Arm64RegisterContext ctx = emulator.getContext();
//                if (address == module.base+0x1FF4){
//                    Pointer md5Ctx = ctx.getPointerArg(0);
//                    Inspector.inspect(md5Ctx.getByteArray(0,32),"MD5Ctx");
//                    Pointer plainText = ctx.getPointerArg(1);
//                    Inspector.inspect(plainText.getByteArray(0,32),"plainText");
//                    int length = ctx.getIntArg(2);
//                    Inspector.inspect("length :",length);
//
//                }else if (address == module.base+0x2004){
//                    Pointer md5Ctx = ctx.getPointerArg(0);
//                    Inspector.inspect(md5Ctx.getByteArray(0,32),"MD5Ctx 222");
//                    Pointer cipherText = ctx.getPointerArg(1);
//                    Inspector.inspect(cipherText.getByteArray(0,32),"cipherText");
//
//                }
//            }
//
//            @Override
//            public void onAttach(UnHook unHook) {
//
//            }
//
//            @Override
//            public void detach() {
//
//            }
//        },module.base+ 0x1FE8, module.base+0x2008,"hengdin");


        StringObject md5_retval = NativeHelperUtils.callStaticJniMethodObject(emulator, "md5(Ljava/lang/String;)Ljava/lang/String;", new StringObject(vm,"123456")); // 执行Jni方法
        System.out.println("md5_retval res : " + md5_retval.getValue());


    }

}
